package com.reggie.lotterysystem.service.impl;

import com.reggie.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.reggie.lotterysystem.common.exception.ServiceException;
import com.reggie.lotterysystem.common.utils.JacksonUtil;
import com.reggie.lotterysystem.common.utils.RedisUtil;
import com.reggie.lotterysystem.controller.param.CreateActivityParam;
import com.reggie.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.reggie.lotterysystem.controller.param.CreateUserByActivityParam;
import com.reggie.lotterysystem.controller.param.PageParam;
import com.reggie.lotterysystem.dao.dataobject.ActivityDO;
import com.reggie.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.reggie.lotterysystem.dao.dataobject.ActivityUserDO;
import com.reggie.lotterysystem.dao.dataobject.PrizeDO;
import com.reggie.lotterysystem.dao.mapper.*;
import com.reggie.lotterysystem.service.ActivityService;
import com.reggie.lotterysystem.service.dto.ActivityDTO;
import com.reggie.lotterysystem.service.dto.ActivityDetailDTO;
import com.reggie.lotterysystem.service.dto.CreateActivityDTO;
import com.reggie.lotterysystem.service.dto.PageListDTO;
import com.reggie.lotterysystem.service.enumclass.ActivityPrizeStatusEnum;
import com.reggie.lotterysystem.service.enumclass.ActivityPrizeTiersEnum;
import com.reggie.lotterysystem.service.enumclass.ActivityStatusEnum;
import com.reggie.lotterysystem.service.enumclass.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements ActivityService{

    private static final Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);

    /**
     * 活动缓存前置，为了区分业务
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";

    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 3L;


    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;



    @Override
    @Transactional(rollbackFor = Exception.class) //涉及多表
    public CreateActivityDTO createActivite(CreateActivityParam param) {
        //1.校验活动信息是否准确
        checkActivityInfo(param);
        //2,保存活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);
        //3.保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = prizeParams.stream()
                .map(prizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        //4.保存活动关联的人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = userParams.stream()
                .map(userParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);
        //5.整合完整的活动信息，存放redis
        //activityId:ActivityDetailDTO:活动 +奖品+人员

        //先获取奖品基本属性列表.
        //获取需要查询的奖品id
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);

        ActivityDetailDTO detilDTO = convertToActivityDetailDTO(activityDO,activityPrizeDOList
                                                                ,activityUserDOList,prizeDOList);
        cacheActivity(detilDTO);

        //6.构造返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //获得总量
        int count = activityMapper.count();

        //获取当前页列表
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.offset(),param.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setStatus(ActivityStatusEnum.forname(activityDO.getStatus()));
                    activityDTO.setDescription(activityDO.getDescription());
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(count,activityDTOList);
    }

    @Override
    public void cacheActivity(Long activityId) {
        if (null == activityId){
            logger.warn("要缓存的活动id为空！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }

        //查询表数据：活动表，关联奖品，关联人员，奖品信息表
        ActivityDO activityDO =  activityMapper.selectById(activityId);
        if (null == activityDO){
            logger.error("要缓存的活动id有误！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }

        //活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        //奖品表：先获取要查询的奖品id
        List<Long> prizeIds = apDOList.stream()
                .map(ActivityPrizeDO::getActivityId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合活动详细信息，存放redis
        cacheActivity(
                convertToActivityDetailDTO(activityDO,apDOList,
                        auDOList,prizeDOList)
        );

    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if (null == activityId){
            logger.warn("查询活动详细信息失败，activityId 为空！");
            return null;
        }
        //查询redis
        ActivityDetailDTO detailDTO = getActivityFromCache(activityId);
        if (null != detailDTO){
            logger.info("查询活动详细信息成功！detailDTO = {}",
                    JacksonUtil.writeValueAsString(detailDTO));
            return detailDTO;
        }

        //如果redis不存在，查表
        //活动表
        ActivityDO aDO = activityMapper.selectById(activityId);

        //活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);

        //活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);

        //奖品表：先获取要查询的奖品id
        List<Long> prizeIds = apDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合活动详细信息，存放redis
        detailDTO = convertToActivityDetailDTO(aDO,apDOList,auDOList,prizeDOList);
        cacheActivity(detailDTO);

        //返回
        return detailDTO;

    }

    /**
     * 根据活动id从缓存中获取活动的详细信息
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if (null == activityId){
            logger.warn("获取缓存活动数据的 activityId 为空！");
            return null;
        }
        try {
            String str = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if (!StringUtils.hasText(str)){
                logger.info("获取的缓存活动数据为空！ key = {}",ACTIVITY_PREFIX + activityId);
                return null;
            }
            return JacksonUtil.readValue(str,ActivityDetailDTO.class);
        }catch (Exception e){
             logger.error("从缓存中获取活动信息异常，key = {}",ACTIVITY_PREFIX + activityId
                                                            ,e);
            return null;
        }

    }

    /**
     * 缓存完整的活动信息 ActivityDetailDTO
     * @param detilDTO
     */
    private void cacheActivity(ActivityDetailDTO detilDTO) {
        //key : ACTIVITY_12
        //value: ActivityDetailDTO(json)
        if (null == detilDTO || null == detilDTO.getActivityId()){
            logger.warn("要缓存的活动信息不存在！");
            return;
        }

        try{
            redisUtil.set(ACTIVITY_PREFIX + detilDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detilDTO),
                    ACTIVITY_TIMEOUT);
        }catch (Exception e){
            logger.error("活动缓存异常，ActivityDetailDTO：{}",
                    JacksonUtil.writeValueAsString(detilDTO)
                    ,e);
        }

    }

    /**
     * 根据基本DO整合完整的活动信息  ActivityDetailDTO
     * @param activityDO
     * @param activityPrizeDOList
     * @param activityUserDOList
     * @param prizeDOList
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityPrizeDO> activityPrizeDOList,
                                                         List<ActivityUserDO> activityUserDOList,
                                                         List<PrizeDO> prizeDOList) {
        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forname(activityDO.getStatus()));

        // apDo: {prizeId; amount, status}, {prizeId; amount, status}
        // pDo: {prizeid, name....)s{prizeid, name....J,{prizeid, name....}
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList.stream()
                .map(activityPrizeDO ->{
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(activityPrizeDO.getPrizeId());
                    /**
                     * @TODO 没懂
                     */
                    Optional<PrizeDO> optionPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(activityPrizeDO.getPrizeId()))
                            .findFirst();
                    //如果PrizeDO为空，不执行当前方法，不为空才执行
                    optionPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forname(activityPrizeDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(activityPrizeDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream()
                .map(auDO ->{
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(auDO.getUserId());
                    userDTO.setUserName(auDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setUserDTOList(userDTOList);
        return activityDetailDTO;
    }

    /**
     * 检验活动有效性
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if (null == param){
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        //检验人员id是否在人员表中存在
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> exitsUserIds = userMapper.selectExistByIds(userIds);
        if (CollectionUtils.isEmpty(exitsUserIds)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(userId ->{
            if (!exitsUserIds.contains(userId)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        //检验奖品id在奖品表中是否存在
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);
        if (null == existPrizeIds){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(prizeId ->{
            if (!existPrizeIds.contains(prizeId)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //人员数量大于等于奖品数量
        int userAmount = param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if (userAmount < prizeAmount){
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }

        //检验活动奖品等级有效性
        param.getActivityPrizeList().
                forEach(prize ->{
            if (null == ActivityPrizeTiersEnum.forname(prize.getPrizeTiers())){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIRES_ERROR);
            }
        });
    }
}
